/*-
 * Copyright (c) 2006, 2007 FTS Team
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 4. Neither the name of the FTS Team nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      Document.hpp
 *
 * $FTS$
 */
#ifndef _DOCUMENT_HPP__
#define _DOCUMENT_HPP__ 1

/**
  @file Document.hpp
  @brief Parsed document body
*/
#include <Types.h>

#include <stdlib.h>

namespace FTSS // FullTextSearchSystem
{

#define C_PART_SIZE (1 << 16)

/**
  @struct DocumentPart Document.hpp <Document.hpp>
  @brief Part of document
*/
struct DocumentPart
{
	/** Start offset of data  */
	UINT_32     data_offset;
	/** Start offset of index */
	UINT_32     index_offset;

	/**
	  @brief Constructor
	  @param iDataOffset - data offset
	  @param iIndexOffset - offset of index
	*/
	inline DocumentPart(const UINT_32 & iDataOffset, const UINT_32 & iIndexOffset) throw(): data_offset(iDataOffset), index_offset(iIndexOffset)
	{
		;;
	}
};

/**
  @struct DocumentIndex Document.hpp <Document.hpp>
  @brief Generic information about stored document
*/
struct DocumentIndex
{
	/** Index field */
	UINT_32     index;
	/**
	  @brief Constructor
	  @param iOffset - data offset from current document part
	  @param iLength - data length
	*/
	inline DocumentIndex(const UINT_32 & iOffset, const UINT_32 & iLength) throw(): index(((iOffset & 0x0000FFFF) << 16) | (iLength & 0x0000FFFF))
	{
		;;
	}

	/**
	  @brief Copy Constructor
	  @param oDocumentIndex - DocumentIndex object
	*/
	inline DocumentIndex(const DocumentIndex & oDocumentIndex) throw(): index(oDocumentIndex.index) { ;; }

	/**
	  @brief Operator =
	  @param oDocumentIndex - object to copy
	  @return reference to self
	*/
	inline DocumentIndex & operator=(const DocumentIndex & oDocumentIndex) throw() { index = oDocumentIndex.index; return *this; }

	/**
	  @brief Get offset of data
	  @return Data offset in current document part
	*/
	inline UINT_32 GetOffset() const throw() { return ((index & 0xFFFF0000) >> 16); }

	/**
	  @brief Get offset of data
	  @return Data offset in current document part
	*/
	inline UINT_32 GetLength() const throw() { return (index & 0x0000FFFF); }
};

/**
  @struct Document Document.hpp <Document.hpp>
  @brief Generic information about stored document
  <pre>
  Stored document data format.
  [4 OCTETS] - full document size
  [4 OCTETS] - size of parts subsection
  [4 OCTETS] - size of index subsection
  [parts_size OCTETS] - data parts
  	[4 OCTETS]   data_offset
	[4 OCTETS]   index_offset
	...
  	[4 OCTETS]   data_offset
	[4 OCTETS]   index_offset
  [index_size OCTETS] - index
  	[2 OCTETS]   data_offset (offset in current part, max. 65535)
  	[2 OCTETS]   data_size   (offset in current part, max. 65535)
  [(size - index_size - parts_size - 3 * 4 OCTETS) OCTETS] - raw document data

  [(size - parts_size - index_size) OCTETS] - full document size
  </pre>
*/
struct Document
{
	/** Document size                */
	UINT_32        size;
	/** Number of parts in document  */
	UINT_32        parts_size;
	/** Index size                   */
	UINT_32        index_size;
	/** Fake start of document parts */
	DocumentPart   parts[1];

	// Fake data structure
	// DocumentIndex index[1];
	// CHAR_8        data[1];

	/**
	  @brief Get document data
	  @return Pointer to document
	*/
	inline CHAR_P DocumentData() throw()
	{
		return (CHAR_P)(&size) + (sizeof(UINT_32) * 3 + parts_size + index_size);
	}

	/**
	  @brief Get document data, constant pointer
	  @return Constant pointer to document
	*/
	inline CCHAR_P DocumentData() const throw()
	{
		return (CCHAR_P)(&size) + (sizeof(UINT_32) * 3 + parts_size + index_size);
	}

	/**
	  @brief Get document data pointer
	  @return Pointer to document
	*/
	inline DocumentIndex * GetDocumentIndex() throw()
	{
		return (DocumentIndex *)((CHAR_P)&size + sizeof(UINT_32) * 3 + parts_size);
	}

	/**
	  @brief Get document data, constant pointer
	  @return Constant pointer to document
	  Document contents begins from index_size offset.
	*/
	inline const DocumentIndex * GetDocumentIndex() const throw()
	{
		return (DocumentIndex *)((CHAR_P)&size + sizeof(UINT_32) * 3 + parts_size);
	}

	/**
	  @brief Get term from specified position
	  @param iPos - term position (in)
	  @param iTermLength - term length (out)
	  @return Pointer to the term
	*/
	inline CHAR_P GetTerm(const UINT_32 & iPos, UINT_32 & iTermLength) throw()
	{
		if (iPos >= index_size / sizeof(UINT_32)) { iTermLength = 0; return NULL; }

		// Get part
		UINT_32 iPart = 0;
		while (iPart < parts_size / sizeof(DocumentPart) && parts[iPart].index_offset <= iPos) { ++iPart; }
		UINT_32 iDataOffset = iPart > 0 ? parts[iPart - 1].data_offset : 0;

		// Index
		DocumentIndex * aIndex = GetDocumentIndex();
		// Term Length
		iTermLength = aIndex[iPos].GetLength();
		// Pointer to data
		return DocumentData() + iDataOffset + aIndex[iPos].GetOffset();
	}

	/**
	  @brief Get term from specified position
	  @param iStartPos - starting position (in)
	  @param iEndPos - ending position (in)
	  @param iRangeLength - term range length (out)
	  @return Pointer to the term
	*/
	CHAR_P GetTermRange(const UINT_32 & iStartPos, UINT_32 iEndPos, UINT_32 & iRangeLength) throw();	

	/**
	  @brief Create document with specified number of terms
	  @param iParts - number parts in document
	  @param iTokens - of terms in document
	  @param iDataSize - length of document
	  @return Pointer to the document object
	*/
	inline static Document * CreateDocument(const UINT_32 & iParts, const UINT_32 & iTokens, const UINT_32 & iDataSize)
	{

		//                   size + parts_size + index_size
		UINT_32 iStructLen = sizeof(UINT_32) * 3 +
		//                   parts
		                     sizeof(DocumentPart) * iParts   +
		//                   index
		                     sizeof(DocumentIndex) * (iTokens + 1) +
		                     iDataSize;

		Document * pDocument    = (Document *)::operator new(iStructLen);
		pDocument -> size       = iStructLen;
		pDocument -> parts_size = iParts  * sizeof(DocumentPart);
		pDocument -> index_size = (iTokens + 1) * sizeof(DocumentIndex);

	return pDocument;
	}

	/**
	  @brief Destroy document and free used memory
	  @param pDocument - document to destroy
	*/
	inline static void DestroyDocument(Document * pDocument) { ::operator delete(pDocument); }
};

} // namespace FTSS
#endif // _DOCUMENT_INFO_HPP__
// End.
